import requests
import json
import time
import hmac
import hashlib
import base64
import urllib.parse
from typing import Optional, Union, List, Dict


class DingTalkSender:
    """
    钉钉消息发送器

    使用说明：
    1. 在钉钉群中添加自定义机器人，获取Webhook地址和加签密钥(可选)
    2. 初始化时传入Webhook地址和加签密钥
    3. 调用相应方法发送消息

    示例：
    >>> sender = DingTalkSender(webhook="your_webhook_url", secret="your_secret")
    >>> sender.send_text("Hello, DingTalk!")
    """

    def __init__(self, webhook: str, secret: Optional[str] = None):
        """
        初始化

        :param webhook: 钉钉机器人Webhook地址
        :param secret: 加签密钥(可选)，如果机器人设置了加签则需要提供
        """
        self.webhook = webhook
        self.secret = secret

    def _generate_signature(self) -> str:
        """生成加签签名"""
        if not self.secret:
            return ""

        timestamp = str(round(time.time() * 1000))
        secret_enc = self.secret.encode('utf-8')
        string_to_sign = f"{timestamp}\n{self.secret}"
        string_to_sign_enc = string_to_sign.encode('utf-8')
        hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
        sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
        return f"&timestamp={timestamp}&sign={sign}"

    def _send_request(self, data: dict) -> dict:
        """发送请求到钉钉"""
        url = self.webhook + self._generate_signature()
        headers = {"Content-Type": "application/json"}
        response = requests.post(url, headers=headers, data=json.dumps(data))
        return response.json()

    def send_text(self, content: str, at_mobiles: Optional[List[str]] = None, at_all: bool = False) -> dict:
        """
        发送文本消息

        :param content: 消息内容
        :param at_mobiles: 要@的手机号列表
        :param at_all: 是否@所有人
        :return: 钉钉API响应
        """
        data = {
            "msgtype": "text",
            "text": {
                "content": content
            },
            "at": {
                "atMobiles": at_mobiles or [],
                "isAtAll": at_all
            }
        }
        return self._send_request(data)

    def send_markdown(self, title: str, text: str, at_mobiles: Optional[List[str]] = None,
                      at_all: bool = False) -> dict:
        """
        发送Markdown格式消息

        :param title: 消息标题
        :param text: Markdown格式内容
        :param at_mobiles: 要@的手机号列表
        :param at_all: 是否@所有人
        :return: 钉钉API响应
        """
        data = {
            "msgtype": "markdown",
            "markdown": {
                "title": title,
                "text": text
            },
            "at": {
                "atMobiles": at_mobiles or [],
                "isAtAll": at_all
            }
        }
        return self._send_request(data)

    def send_link(self, title: str, text: str, message_url: str, pic_url: Optional[str] = None) -> dict:
        """
        发送链接消息

        :param title: 消息标题
        :param text: 消息内容
        :param message_url: 点击消息跳转的URL
        :param pic_url: 图片URL(可选)
        :return: 钉钉API响应
        """
        data = {
            "msgtype": "link",
            "link": {
                "title": title,
                "text": text,
                "messageUrl": message_url,
                "picUrl": pic_url or ""
            }
        }
        return self._send_request(data)

    def send_action_card(
            self,
            title: str,
            text: str,
            single_title: Optional[str] = None,
            single_url: Optional[str] = None,
            btn_orientation: str = "0",
            btns: Optional[List[Dict[str, str]]] = None
    ) -> dict:
        """
        发送ActionCard消息

        :param title: 标题
        :param text: 内容
        :param single_title: 单个按钮标题(使用单一按钮时)
        :param single_url: 单个按钮链接(使用单一按钮时)
        :param btn_orientation: 按钮排列方向 0-竖直，1-横向
        :param btns: 按钮列表(使用多个按钮时) [{"title": "按钮1", "actionURL": "url1"}, ...]
        :return: 钉钉API响应
        """
        data = {
            "msgtype": "actionCard",
            "actionCard": {
                "title": title,
                "text": text,
                "btnOrientation": btn_orientation
            }
        }

        if single_title and single_url:
            data["actionCard"]["singleTitle"] = single_title
            data["actionCard"]["singleURL"] = single_url
        elif btns:
            data["actionCard"]["btns"] = btns

        return self._send_request(data)

    def send_feed_card(self, links: List[Dict[str, str]]) -> dict:
        """
        发送FeedCard消息

        :param links: 链接列表 [{"title": "标题1", "messageURL": "url1", "picURL": "pic1"}, ...]
        :return: 钉钉API响应
        """
        data = {
            "msgtype": "feedCard",
            "feedCard": {
                "links": links
            }
        }
        return self._send_request(data)